﻿using System.Linq;
using JamesZhao.Engine.Graphics.Effects;
using JamesZhao.Engine.Graphics.Geometry;
using JamesZhao.Engine.Graphics.Surfaces;
using JamesZhao.Engine.Graphics.Visuals;
using SlimDX.Direct3D11;
using SlimDX.DXGI;
using SlimMath;
using Vector3 = SlimDX.Vector3;
using Vector4 = SlimDX.Vector4;

namespace JamesZhao.Engine.Graphics.RenderStages
{
    internal class DeferredShadingStage : RenderStage
    {
        private readonly GraphicsDevice _device;
        private EffectResourceVariable _varAlbedoTexture;
        private EffectResourceVariable _varDepthTexture;
        private EffectVectorVariable _varLightPosIntensity;
        private EffectResourceVariable _varNormalTexture;
        private EffectScalarVariable _varNumOfLights;
        private EffectVectorVariable _vargBufferSize;

        public DeferredShadingStage(GraphicsDevice device)
        {
            _device = device;
            DShade = new GpuProgram(device, "Shaders/DShade.fx");
            var quadGeom = new GeometryBuffer(device);
            quadGeom.InitializeFrom(MeshData.LoadFullScreenQuad());
            FullScreenQuad = new MeshDrawable
                                 {
                                     GeometryBuffer = quadGeom
                                 };
        }

        public GpuProgram DShade { get; private set; }
        public MeshDrawable FullScreenQuad { get; private set; }

        public GBuffer GBuffer { get; set; }

        public ColorSurface ColorSurface { get; set; }

        public override string Name
        {
            get { return "Deferred Shading"; }
        }

        protected override void OnRender(GraphicsDevice device, SwapChain swapChain, VisualsList visualsList,
                                         Camera camera)
        {
            if (_varNormalTexture == null)
            {
                _varNormalTexture = DShade.Effect.GetVariableByName("normalTexture").AsResource();
                _varDepthTexture = DShade.Effect.GetVariableByName("depthTexture").AsResource();
                _varAlbedoTexture = DShade.Effect.GetVariableByName("albedoTexture").AsResource();
                _vargBufferSize = DShade.Effect.GetVariableByName("gBufferSize").AsVector();
                _varLightPosIntensity = DShade.Effect.GetVariableByName("lightPosIntensity").AsVector();
                _varNumOfLights = DShade.Effect.GetVariableByName("numOfLights").AsScalar();
            }

            _varNormalTexture.SetResource(GBuffer.Normal.ResourceView);
            _varDepthTexture.SetResource(GBuffer.Depth.ResourceView);
            _varAlbedoTexture.SetResource(GBuffer.Albedo.ResourceView);
            _vargBufferSize.Set(new Vector2(GBuffer.Normal.ActualSize.Width, GBuffer.Normal.ActualSize.Height));


            Vector4[] lightsInput = visualsList.Lights
                .Where(x => x.IsShadowed == false)
                .Select(x =>
                            {
                                var pos = new SlimMath.Vector4(x.LightCamera.Position, 1);
                                SlimMath.Vector4 vPos = SlimMath.Vector4.Transform(pos, camera.ViewMatrix);
                                return new Vector4(new Vector3(vPos.X, vPos.Y, vPos.Z), x.Intensity);
                            }
                ).ToArray();
//            foreach (var vector4 in lightsInput)
//            {
//                Console.WriteLine(vector4);
//            }
            _varNumOfLights.Set(lightsInput.Length);
            if (lightsInput.Length > 0)
            {
                _varLightPosIntensity.Set(lightsInput);
            }


            device.ImmediateContext.OutputMerger.SetTargets(ColorSurface.TargetView);

            BlendState oldBlendState = device.ImmediateContext.OutputMerger.BlendState;
            FullScreenQuad.RenderWithGpuProgram(device, camera, DShade);
            device.ImmediateContext.OutputMerger.BlendState = oldBlendState;
        }
    }
}